/***
 
	Lib3ds port to alchemy.
 
 ***/
#include <AS3.h>
#include <stdio.h>
#include "lib3ds/types.h"
#include "lib3ds/file.h"
#include "lib3ds/chunk.h"
#include "lib3ds/mesh.h"
#include "lib3ds/material.h"
#include "lib3ds/node.h"
#include "lib3ds/light.h"
#include "lib3ds/camera.h"
#include <stdlib.h>
#include <string.h>

void sztrace(char* msg);

AS3_Val createTexel(float s, float t)
{
	return AS3_Array("DoubleType, DoubleType", s, t);
}
		
AS3_Val createVector3D(float x, float y, float z)
{
	return AS3_Array("DoubleType, DoubleType, DoubleType", x, y, z);
}

AS3_Val createLib3dsInstance( char *className )
{
	AS3_Val package = AS3_String("com.floorplanner.lib3ds");
	AS3_Val meshClass = AS3_NSGetS(package, className);
	AS3_Val params = AS3_Array("");
	AS3_Val instance = AS3_New(meshClass, params);
	
	AS3_Release(package);
	AS3_Release(meshClass);
	AS3_Release(params);
	
	if (instance == NULL) {
		fprintf(stderr, "***ERROR***\nCould not instantiate \"com.floorplanner.lib3ds.%s\".\nPlease make sure this class is compiled into your swf!\n", className);
	}
	
	return instance;
}

AS3_Val readMatrix(Lib3dsMatrix matrix)
{
	AS3_Val ret = AS3_Array("");
	int i;
	
	for(i = 0; i < 4; i++)
	{
		AS3_Val row = AS3_Array("DoubleType, DoubleType, DoubleType, DoubleType",
								matrix[i][0], matrix[i][1], matrix[i][2], matrix[i][3]);
		AS3_CallS("push", ret, row);
	}
	
	return ret;
}

AS3_Val readNodes(Lib3dsNode *parent)
{
	AS3_Val ret = AS3_Array("");
	AS3_Val children = AS3_Array("");
	Lib3dsNode *node;
	
	if(parent == NULL) {
		return AS3_Null();
	}
	
	for(node = parent->childs; node != NULL; node = node->next) {
		AS3_Val t = readNodes(node);
		if(t != NULL) {
			AS3_CallS("push", children, AS3_Array("AS3ValType", t));
		}
	}
	
	AS3_Val asnode = AS3_Object("name:StrType, children:AS3ValType, type:IntType, node_id:IntType, flags1:IntType, flags2:IntType, parent_id:IntType, matrix:AS3ValType", 
		parent->name, children, parent->type, parent->node_id, parent->flags1, parent->flags2, parent->parent_id, 
		readMatrix(parent->matrix));
	
	AS3_CallS("push", ret, AS3_Array("AS3ValType", asnode));
	
	return ret;
};

void readMeshFaces( Lib3dsMesh *mesh, AS3_Val asMesh )
{
	AS3_Val faces = AS3_Array("");
	int i;
	
	AS3_SetS(asMesh, "faces", AS3_Int(mesh->faces));
	
	for(i = 0; i < mesh->faces; i++) {
		Lib3dsFace face = mesh->faceL[i];
		AS3_Val points = AS3_Array("IntType, IntType, IntType", face.points[0], face.points[1], face.points[2]);
		AS3_Val normal = createVector3D(face.normal[0], face.normal[1], face.normal[2]);
		AS3_Val asFace = createLib3dsInstance("Lib3dsFace");
		
		AS3_SetS(asFace, "material", AS3_String(face.material));
		AS3_SetS(asFace, "flags", AS3_Int(face.flags));
		AS3_SetS(asFace, "smoothing", AS3_Int(face.smoothing));
		AS3_SetS(asFace, "points", points);
		AS3_SetS(asFace, "normal", normal);
		
		AS3_CallS("push", faces, AS3_Array("AS3ValType", asFace));
	}
	
	AS3_SetS(asMesh, "faceL", faces);
};

void readMeshPoints( Lib3dsMesh *mesh, AS3_Val asMesh )
{
	AS3_Val points = AS3_Array("");
	int i;
	
	AS3_SetS(asMesh, "points", AS3_Int(mesh->points));
	
	for(i = 0; i < mesh->points; i++) {
		Lib3dsPoint pt = mesh->pointL[i];
		AS3_Val value = createVector3D(pt.pos[0], pt.pos[1], pt.pos[2]);
		AS3_CallS("push", points, AS3_Array("AS3ValType", value));
	}
	
	AS3_SetS(asMesh, "pointL", points);
};

void readMeshTexels( Lib3dsMesh *mesh, AS3_Val asMesh )
{
	AS3_Val texels = AS3_Array("");
	int i;
	
	AS3_SetS(asMesh, "texels", AS3_Int(mesh->texels));
	
	for(i = 0; i < mesh->texels; i++) {
		AS3_Val value = createTexel(mesh->texelL[i][0], mesh->texelL[i][1]);
		AS3_CallS("push", texels, AS3_Array("AS3ValType", value));
	}
	
	AS3_SetS(asMesh, "texelL", texels);
};

void readMesh( Lib3dsMesh *mesh, AS3_Val asMesh )
{
	AS3_SetS(asMesh, "name", AS3_String(mesh->name));
	
	readMeshPoints(mesh, asMesh);
	readMeshFaces(mesh, asMesh);
	readMeshTexels(mesh, asMesh);
};

void readMaterial( Lib3dsMaterial *material, AS3_Val asMaterial )
{
	AS3_Val ambient = createLib3dsInstance("Lib3dsRgba");
	AS3_Val diffuse = createLib3dsInstance("Lib3dsRgba");
	AS3_Val specular = createLib3dsInstance("Lib3dsRgba");
	
	AS3_SetS(ambient, "r", AS3_Number(material->ambient[0]));
	AS3_SetS(ambient, "g", AS3_Number(material->ambient[1]));
	AS3_SetS(ambient, "b", AS3_Number(material->ambient[2]));
	AS3_SetS(ambient, "a", AS3_Number(material->ambient[3]));

	AS3_SetS(diffuse, "r", AS3_Number(material->diffuse[0]));
	AS3_SetS(diffuse, "g", AS3_Number(material->diffuse[1]));
	AS3_SetS(diffuse, "b", AS3_Number(material->diffuse[2]));
	AS3_SetS(diffuse, "a", AS3_Number(material->diffuse[3]));
	
	AS3_SetS(specular, "r", AS3_Number(material->specular[0]));
	AS3_SetS(specular, "g", AS3_Number(material->specular[1]));
	AS3_SetS(specular, "b", AS3_Number(material->specular[2]));
	AS3_SetS(specular, "a", AS3_Number(material->specular[3]));
	
	AS3_SetS(asMaterial, "name", AS3_String(material->name));
	AS3_SetS(asMaterial, "ambient", ambient);
	AS3_SetS(asMaterial, "diffuse", diffuse);
	AS3_SetS(asMaterial, "specular", specular);
	AS3_SetS(asMaterial, "shininess", AS3_Number(material->shininess));
	AS3_SetS(asMaterial, "shin_strength", AS3_Number(material->shin_strength));
	AS3_SetS(asMaterial, "use_blur", (material->use_blur != 0 ? AS3_True() : AS3_False()));
	AS3_SetS(asMaterial, "blur", AS3_Number(material->blur));
	AS3_SetS(asMaterial, "transparency", AS3_Number(material->transparency));
	AS3_SetS(asMaterial, "falloff", AS3_Number(material->falloff));
	AS3_SetS(asMaterial, "additive", (material->additive != 0 ? AS3_True() : AS3_False()));
	AS3_SetS(asMaterial, "self_ilpct", AS3_Number(material->self_ilpct));
	AS3_SetS(asMaterial, "use_falloff", (material->use_falloff != 0 ? AS3_True() : AS3_False()));
	AS3_SetS(asMaterial, "self_illum", (material->self_illum != 0 ? AS3_True() : AS3_False()));
	AS3_SetS(asMaterial, "shading", AS3_Int(material->shading));
	AS3_SetS(asMaterial, "soften", (material->soften != 0 ? AS3_True() : AS3_False()));
	AS3_SetS(asMaterial, "face_map", (material->face_map != 0 ? AS3_True() : AS3_False()));
	AS3_SetS(asMaterial, "two_sided", (material->two_sided != 0 ? AS3_True() : AS3_False()));
	AS3_SetS(asMaterial, "map_decal", (material->map_decal != 0 ? AS3_True() : AS3_False()));
	AS3_SetS(asMaterial, "use_wire", (material->use_wire != 0 ? AS3_True() : AS3_False()));
	AS3_SetS(asMaterial, "use_wire_abs", (material->use_wire_abs != 0 ? AS3_True() : AS3_False()));
	AS3_SetS(asMaterial, "wire_size", AS3_Number(material->wire_size));
};

AS3_Val readFile( void *data, AS3_Val args )
{
	char * fileName;
	Lib3dsFile *file = 0;
	Lib3dsMesh *mesh = 0;
	Lib3dsMaterial *material;
	Lib3dsNode *node;
	Lib3dsCamera *camera;
	Lib3dsLight *light;
	AS3_Val asFile = createLib3dsInstance("Lib3dsFile");
	AS3_Val asMesh = createLib3dsInstance("Lib3dsMesh");
	AS3_Val asMaterial = createLib3dsInstance("Lib3dsMaterial");
	AS3_Val asNode = createLib3dsInstance("Lib3dsNode");
	AS3_Val asLight = createLib3dsInstance("Lib3dsCamera");
	AS3_Val asCamera = createLib3dsInstance("Lib3dsLight");
	AS3_Val ambient = createLib3dsInstance("Lib3dsRgba");
	
	// make sure we've got all required AS3 classes
	if (asFile == NULL || asMesh == NULL || asMaterial == NULL || asNode == NULL || asLight == NULL || asCamera == NULL ||
		ambient == NULL) {
		return AS3_Null();
	}
	
	// grab our filename
	AS3_ArrayValue(args, "StrType", &fileName);
	
	// file should be supplied by "supplyFile"
	file = lib3ds_file_load(fileName);
	if (!file) {
		fprintf(stderr, "***ERROR***\nLoading file %s failed\n", fileName);
		return AS3_Null();
	}
	
	// populate the AS3 Lib3dsFile object
	AS3_SetS(asFile, "mesh_version", AS3_Int(file->mesh_version));
	AS3_SetS(asFile, "keyf_revision", AS3_Int(file->keyf_revision));
	AS3_SetS(asFile, "name", AS3_String(file->name));
	AS3_SetS(asFile, "master_scale", AS3_Number(file->master_scale));
	AS3_SetS(asFile, "frames", AS3_Int(file->frames));
	AS3_SetS(asFile, "segment_from", AS3_Int(file->segment_from));
	AS3_SetS(asFile, "segment_to", AS3_Int(file->segment_to));
	AS3_SetS(asFile, "current_frame", AS3_Int(file->current_frame));
	AS3_SetS(asFile, "construction_plane", createVector3D(file->construction_plane[0], file->construction_plane[1], file->construction_plane[2]));
	
	if (file->ambient && (file->ambient[0] || file->ambient[1] || file->ambient[2] || file->ambient[3])) {
		AS3_SetS(ambient, "r", AS3_Number(file->ambient[0]));
		AS3_SetS(ambient, "g", AS3_Number(file->ambient[1]));
		AS3_SetS(ambient, "b", AS3_Number(file->ambient[2]));
		AS3_SetS(ambient, "a", AS3_Number(file->ambient[3]));
		AS3_SetS(asFile, "ambient", ambient);
	}
	
	if (file->meshes) {
		AS3_SetS(asFile, "meshes", asMesh);
	}
	if (file->materials) {
		AS3_SetS(asFile, "materials", asMaterial);
	}
	if (file->cameras) {
		AS3_SetS(asFile, "cameras", asCamera);
	}
	if (file->lights) {
		AS3_SetS(asFile, "lights", asLight);
	}
	if (file->nodes) {
		AS3_SetS(asFile, "nodes", asNode);
	}
	
	// materials
	for (material = file->materials; material != NULL; material = material->next) {
		
		readMaterial(material, asMaterial);
		
		if (material->next) {
			AS3_Val nextMaterial = createLib3dsInstance("Lib3dsMaterial");
			// move on to the next mesh
			AS3_SetS(asMaterial, "next", nextMaterial);
			asMaterial = nextMaterial;
		}
	}
	
	// meshes
	for (mesh = file->meshes; mesh != NULL; mesh = mesh->next) {
		
		readMesh(mesh, asMesh);
		
		if (mesh->next != NULL) {
			// move on to the next mesh
			AS3_Val nextMesh = createLib3dsInstance("Lib3dsMesh");
			AS3_SetS(asMesh, "next", nextMesh);
			asMesh = nextMesh;
		}
	}

	// cameras
	for (camera = file->cameras; camera != NULL; camera = camera->next) {
		AS3_SetS(asCamera, "name", AS3_String(camera->name));
		
		if (camera->next != NULL) {
			// move on to the next camera
			AS3_Val nextCamera = createLib3dsInstance("Lib3dsCamera");
			AS3_SetS(asCamera, "next", nextCamera);
			asCamera = nextCamera;
		}
	}
	
	// lights
	for (light = file->lights; light != NULL; light = light->next) {
		
		AS3_SetS(asLight, "name", AS3_String(light->name));
		
		if (light->next != NULL) {
			// move on to the next light
			AS3_Val nextLight = createLib3dsInstance("Lib3dsLight");
			AS3_SetS(asLight, "next", nextLight);
			asLight = nextLight;
		}
	}
			
	// nodes
	for (node = file->nodes; node != NULL; node = node->next) {
		
		AS3_SetS(asNode, "name", AS3_String(node->name));
		
		if (node->next) {
			// move on to the next node
			AS3_Val nextNode = createLib3dsInstance("Lib3dsNode");
			AS3_SetS(asNode, "next", nextNode);
			asNode = nextNode;
		}
	}
	
	return asFile;
}

int main()
{
	AS3_Val readFileAsync_ = AS3_FunctionAsync( NULL, readFile );
	AS3_Val readFile_ = AS3_Function( NULL, readFile );
	AS3_Val airobj = AS3_Object("readFileAsync: AS3ValType, readFile: AS3ValType", 
								readFileAsync_, 
								readFile_);
	AS3_Release( readFileAsync_ );
	AS3_Release( readFile_ );
	AS3_LibInit( airobj );
	return 0;
};